#ifndef __SOFTIIC__H__
#define __SOFTIIC__H__
#include "BasicInterface.h"
#include "stm32f10x.h"

#ifdef USE_BASICIO
class SoftIIC : public BasicIO
#pragma push
#pragma diag_suppress 1300
#else
class SoftIIC
#endif
{
private:
    volatile uint32_t* SDA_I, * SDA_O, * SCK;
    GPIO_TypeDef* SDA_GPIO;
    uint8_t SDA_Num;

    void inline delay() const
    {
        uint8_t aa = 2;
        while (aa--);
    }
    uint32_t getClearBit32(uint8_t num) const
    {
        uint32_t res = 0;
        for (int i = 7; i >= 0; i--)
        {
            res <<= 4;
            if (i == num)
                res &= 0xFFFFFFF0;
            else
                res |= 0x0F;
        }
        return res;
    }
    void setSDAIn() const
    {
        if (this->SDA_Num > 7)
        {
            this->SDA_GPIO->CRH &= this->clearBit;
            this->SDA_GPIO->CRH |= (uint32_t)(0x04 << (this->SDA_Num * 4));
        }
        else
        {
            this->SDA_GPIO->CRL &= this->clearBit;
            this->SDA_GPIO->CRL |= (uint32_t)(0x04 << (this->SDA_Num * 4));
        }
    }
    void setSDAOut() const
    {
        if (this->SDA_Num > 7)
        {
            this->SDA_GPIO->CRH &= this->clearBit;
        }
        else
        {
            this->SDA_GPIO->CRL &= this->clearBit;
            this->SDA_GPIO->CRL |= (uint32_t)(0x03 << (this->SDA_Num * 4));
        }
    }
    void ACK(bool n = true) const
    {
        *this->SCK = 0;
        this->setSDAOut();
        *this->SDA_O = !n;
        Delay.us(1);
        *this->SCK = 1;
        Delay.us(1);
        *this->SCK = 0;
    }
    bool checkACK() const;
public:
    const uint32_t clearBit;
    SoftIIC(GPIO_TypeDef* SDA_GPIOx, uint8_t SDA_Num, GPIO_TypeDef* SCK_GPIOx, uint8_t SCK_Num);
    void generateStart() const
    {
        this->setSDAOut();
        *this->SDA_O = 1;
        *this->SCK = 1;
        *this->SDA_O = 0;
        *this->SCK = 0;
    }
    void generateStop() const
    {
        this->setSDAOut();
        *this->SDA_O = 0;
        *this->SCK = 0;
        *this->SCK = 1;
        *this->SDA_O = 1;
    }
    void writeByte(uint8_t byte) const;
    uint8_t readByte(bool ack = true) const;

#ifdef USE_BASICIO
    virtual Resault Read(const uint32_t address, void* buffer, uint32_t length) const;
    virtual Resault Write(const uint32_t address, const void* buffer, uint32_t length) const;
#pragma pop
#endif
};

#endif
